package org.currentcost.messages;

import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

public class EnviMessage
{
	private Type _type;

	public enum Type
	{
		RealTime, Hourly, Daily, Cost
	}

	private String _version;
	private Integer _daysSinceBirth;
	private Temperature _temperature;
	private String _uid;
	private String _sensorId;
	private String _radioId;
	private Double _impulsePerUnit;

	private SensorData _sensorData;
	private Calendar _time;
	private Integer _daysSinceWipe;
	private EnviUnit _unit;
	private Calendar _referenceHour;
	private static final int HISTORY_HOUR_COUNT = 2;
	private Calendar _referenceDay;

	@Override
	public String toString()
	{
		StringBuilder b = new StringBuilder();
		b.append("Version:\t\t").append(getVersion()).append("\n");
		b.append("Days s. birth:\t").append(getDaysSinceBirth()).append("\n");
		b.append("Time:\t\t\t").append(DateFormat.getDateTimeInstance().format(getTime().getTime())).append("\n");
		if (getTemperature() != null) {
			b.append("Temperature:\t").append(getTemperature().toString()).append("\n");
		}
		if (_uid != null) {
			b.append("UID:\t\t\t").append(getUid()).append("\n");
		}
		b.append("Sensor ID:\t\t").append(getSensorId()).append("\n");
		if (getRadioId() != null) {
			b.append("Radio ID:\t\t").append(getRadioId()).append("\n");
		}
		if (_impulsePerUnit != null) {
			b.append("Impulse per unit:\t").append(_impulsePerUnit).append("\n");
		}
		return b.toString();
	}

	public void setType(Type type)
	{
		_type = type;
	}

	public Type getType()
	{
		return _type;
	}

	public Calendar getTime()
	{
		return _time;
	}

	public String getRadioId()
	{
		return _radioId;
	}

	public String getVersion()
	{
		return _version;
	}

	public void setVersion(String version)
	{
		_version = version;
	}

	public Integer getDaysSinceBirth()
	{
		return _daysSinceBirth;
	}

	public void setDaysSinceBirth(int daysSinceBirth)
	{
		_daysSinceBirth = daysSinceBirth;
	}

	public void setTimeString(String timeString) throws ParseException
	{
		GregorianCalendar cal = new GregorianCalendar();

		Date time = DateFormat.getTimeInstance().parse(timeString);
		Calendar hcal = new GregorianCalendar();
		hcal.setTime(time);
		cal.set(Calendar.HOUR_OF_DAY, hcal.get(Calendar.HOUR_OF_DAY));
		cal.set(Calendar.MINUTE, hcal.get(Calendar.MINUTE));
		cal.set(Calendar.SECOND, hcal.get(Calendar.SECOND));
		_time = cal;
	}

	private Calendar getReferenceHour()
	{
		if (_referenceHour == null) {
			if (_time == null) {
				throw new IllegalStateException("Message time wasn't set yet");
			}
			_referenceHour = (Calendar) _time.clone();
			_referenceHour.set(Calendar.SECOND, 0);
			_referenceHour.set(Calendar.MINUTE, 0);
			if (_referenceHour.get(Calendar.HOUR_OF_DAY) % 2 == 0) {
				_referenceHour.add(Calendar.HOUR_OF_DAY, -1);
			}
		}
		return (Calendar) _referenceHour.clone();
	}

	private Calendar getReferenceDay()
	{
		if (_referenceDay == null) {
			if (_time == null) {
				throw new IllegalStateException("Message time wasn't set yet");
			}
			_referenceDay = (Calendar) _time.clone();
			_referenceDay.set(Calendar.SECOND, 0);
			_referenceDay.set(Calendar.MINUTE, 0);
			_referenceDay.set(Calendar.HOUR_OF_DAY, 0);
		}
		return (Calendar) _referenceDay.clone();
	}

	public Temperature getTemperature()
	{
		return _temperature;
	}

	public String getUid()
	{
		return _uid;
	}

	public void setUid(String uid)
	{
		_uid = uid;
	}

	public String getSensorId()
	{
		return _sensorId;
	}

	public void setSensorId(String sensorId)
	{
		_sensorId = sensorId;
	}

	public Double getImpulsePerUnit()
	{
		return _impulsePerUnit;
	}

	public void setImpulsePerUnit(Double impulsePerUnit)
	{
		_impulsePerUnit = impulsePerUnit;
	}

	public void setTemperature(double value, EnviUnit unit)
	{
		_temperature = new Temperature(value, unit);
	}

	public void addData(String channelId, double value, EnviUnit unit)
	{
		_sensorData.addData(_sensorId, channelId, value, unit);
	}

	public void setSensorType(SensorData.SensorType type)
	{
		if (_sensorData != null) {
			throw new IllegalStateException("This message already has sensor data");
		}
		_sensorData = new SensorData(type);
	}

	public void setRadioId(String radioId)
	{
		_radioId = radioId;
	}

	public void setImpulsePerUnit(double value)
	{
		_impulsePerUnit = value;
	}

	public void setDaysSinceWipe(int daysSinceWipe)
	{
		_daysSinceWipe = daysSinceWipe;
	}

	public void setUnit(EnviUnit unit)
	{
		_unit = unit;
	}

	public void addHourData(String channelId, Integer hoursAgo, double value)
	{
		Calendar cal = getReferenceHour();
		cal.add(Calendar.HOUR_OF_DAY, -hoursAgo);
		Date startDate = cal.getTime();
		cal.add(Calendar.HOUR_OF_DAY, HISTORY_HOUR_COUNT);
		Date endDate = cal.getTime();
		_sensorData.addData(_sensorId, channelId, value, _unit, startDate, endDate);
	}

	public void addDayData(String channelId, Integer daysAgo, double value)
	{
		Calendar cal = getReferenceDay();
		cal.add(Calendar.DAY_OF_MONTH, -daysAgo);
		Date startDate = cal.getTime();
		cal.add(Calendar.DAY_OF_MONTH, 1);
		Date endDate = cal.getTime();
		_sensorData.addData(_sensorId, channelId, value, _unit, startDate, endDate);
	}

	public SensorData getSensorData()
	{
		return _sensorData;
	}

	private class Temperature
	{
		public final double value;
		public final EnviUnit unit;

		public Temperature(double value, EnviUnit unit)
		{
			this.value = value;
			this.unit = unit;
		}

		@Override
		public String toString()
		{
			return MessageFormat.format("{0,number,#.#} {1}", value, unit);
		}
	}

	public static enum EnviMessageType
	{
		REAL_TIME_ELECTRICITY,
		REAL_TIME_METER_IMPULSE,
		TWO_HOUR_HISTORY,
		DAILY_HISTORY,
		MONTHLY_HISTORY
	}
}
